package main

import "fmt"

/*
类型断言就是将接口类型的值(x)，转换成类型(T)。格式为：
	value, ok := x.(T)

注意：
- x的数据类型必须是interface{}类型
- 断言表达式会返回 x 的值（也就是 value）和一个布尔值（也就是 ok），可以根据布尔值判断x是否为一个T类型
- 如果T是某个具体的类型，类型断言会检查 x 的动态类型是否等于具体类型 T。如果检查成功，类型断言返回的结果是 x 的动态值，其类型是 T。
- 如果T是一个接口类型，那么类型断言会检查 x 的动态类型是否满足 T。如果检查成功，x 的动态值不会被提取，返回值是一个类型为 T 的接口值。
- 如果x是nil，那么类型断言直接失败

*/
func main() {
	demo01NormalUse()
	demo02TypeSwitch()
	demo03StructTypeAssert()
}

func demo01NormalUse() {
	var i interface{} = 10
	t1, ok := i.(int)
	fmt.Printf("%d-%t\n", t1, ok) //10-true

	fmt.Println("=====分隔线1=====")

	t2, ok := i.(string)
	fmt.Printf("%s[%T]-%t\n", t2, t2, ok) //[string]-false   (这里t2不是nil，而是一个空字符串)

	fmt.Println("=====分隔线2=====")

	var k interface{}         // nil
	t3, ok := k.(interface{}) // <nil> - false	（nil类型不可以进行类型断言）
	fmt.Println(t3, "-", ok)

	fmt.Println("=====分隔线3=====")
	k = 10
	t4, ok := k.(interface{})
	fmt.Printf("%d-%t\n", t4, ok) // 10-true

	t5, ok := k.(int)
	fmt.Printf("%d-%t\n", t5, ok) // 10-true

}

/*
可以结合switch来判断x的类型

3 is int
sdclone is string
<nil> is nil
3.56 not type matched
*/
func demo02TypeSwitch() {

	findType := func(x interface{}) {
		switch tp := x.(type) {
		case int:
			fmt.Println(tp, "is int")
		case string:
			fmt.Println(tp, "is string")
		case nil:
			fmt.Println(tp, "is nil")
		default:
			fmt.Println(tp, "not type matched")
		}
	}

	findType(3)
	findType("sdclone")
	findType(nil)
	findType(3.56)

}

/*
例如对于一个interface{}类型的变量a，我们给他赋值一个具体的AStruct{}
此时a无法使用AStruct的方法，必须类型断言，将a转换为AStruct类型，才可以使用AStruct的变量和方法
类似于java   Object o; o = new ArrayList<>(); list = (List) o; list.add(...)
*/
func demo03StructTypeAssert() {
	var a interface{}
	a = AStruct{}
	value := a.(AStruct)
	value.Hi()                       // Type Is AStruct
	fmt.Println("看是否输出", value.Name) // 看是否输出 fushaohua

}

type AStruct struct {
	Name string
}

func (a AStruct) String() string {
	return "Type Is AStruct"
}
func (a *AStruct) Hi() {
	a.Name = "fushaohua"
	fmt.Println(a)
}
